package handler

import (
	"bytes"
	"encoding/json"
	"github.com/gorilla/mux"
	"github.com/gorilla/websocket"
	"github.com/mitchellh/mapstructure"
	"github.com/pkg/errors"
	"google.golang.org/protobuf/proto"
	"log"
	"net/http"
	"src/config"
	"src/connection"
	"src/global"
	websocket2 "src/handler/websocket"
	"src/model"
	"src/model/ProtoModel"
	"src/service"
	"src/task"
	"src/utils"
	"strconv"
	"strings"
)

// subscribe 订阅逻辑
type RequestHandler struct {
	Base *service.Req
	Conn *connection.Ws
}

func NewWebSocketHandler() *RequestHandler {
	return &RequestHandler{}
}

// 用户发起请求触发 ws链接 绑定触发
func (this *RequestHandler) RequestHandler(base *service.Req) {
	var err error
	defer func() {
		if err != nil {
			//清除链接状态 以及全局缓存数据
			log.Println(err)
		}
		//conn.Close()
	}()
	this.Base = base
	w := base.W
	r := base.R
	//ip 请求token授权连续失败 禁止链接
	ip := (&utils.Common{}).GetIP(r)
	if (&utils.Common{}).TokenIpWall(ip) == false {
		//w.Write([]byte("wall"))
		log.Println("ip连续异常拦截")
		r.Body.Close()
		return
	}
	var upgrader = websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		// 解决跨域问题
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		//ws 握手 失败
		//log.Println(err)
		return
	}
	conn, err := (&connection.Ws{}).GetConn(ws)

	if err != nil {
		return
	}
	Id := strconv.FormatInt(global.Ws_.Id, 10)
	conn.Id = Id
	//建立全局缓存
	global.Ws_.UpdateConnList(Id, conn)
	//唯一ID自增
	global.Ws_.Inc()

	this.Conn = conn

	reply := &ProtoModel.ReplyBodyProto{
		Key:  "client_handshake",
		Code: "200",
		Data: make(map[string]string),
	}
	//推给前端Cid
	reply.Data["cid"] = conn.Id
	reply.Data["server-id"] = config.V().GetString("server-config.server-id")
	reply.Data["region-id"] = config.V().GetString("server-config.region-id")
	//log.Println(reply)
	(&service.ReplyBody{}).SendMsgToConn(reply, conn.Id)
	err = this.onRequestAct()
	if err != nil {
		log.Println(err)
		reply.Message = err.Error()
		reply.Code = "401"
		(&service.ReplyBody{}).SendMsgToConn(reply, conn.Id)
		return
	}
	err = this.onRequest()
	if err != nil {
		log.Println(err)
		return
	}

}
func (this *RequestHandler) onRequestDispatch(dataBytes []byte, dataType ProtoModel.DataTypeProto, msgFmt string) error {
	//var dataType ProtoModel.DataTypeProto
	//var dataBytes []byte
	var err error
	switch dataType {
	case ProtoModel.DataTypeProto_PONG:

		err = this.onPong(dataBytes, msgFmt)
		//if err != nil {
		//	return err
		//}
	case ProtoModel.DataTypeProto_PING:
		err = this.onPing(dataBytes, msgFmt)

		//if err != nil {
		//	return err
		//}
	case ProtoModel.DataTypeProto_MESSAGE:
		err = this.onMessage(dataBytes, msgFmt)
		//if err != nil {
		//	return err
		//}
	case ProtoModel.DataTypeProto_SENT:

		return this.onSent(dataBytes, msgFmt)
		//if err != nil {
		//	return err
		//}
	case ProtoModel.DataTypeProto_REPLY:
	default:
		err = nil
	}
	return err
}
func (this *RequestHandler) onRequestAct() error {
	var err error
	r := this.Base.R
	//参数
	params := mux.Vars(r)
	act := params["act"]
	if len(act) > 0 {
		value := params["value"]
		if len(value) > 0 {
			switch act {
			case "client_bind":
				//log.Println(token, len(token))
				clientBind := model.ClientBindProto{}
				clientBind.Token = value
				err = (&websocket2.WebSocketService{}).ClientBind(this.Conn, clientBind)
				break
			case "client_bind_cid_test":
				testOpen := config.V().GetBool("test.isOpen")

				if !testOpen {
					return errors.New("配置文件test.isOpen 未开启")
				}
				splitted := strings.Split(value, ",")
				if len(splitted) < 2 {
					return errors.New("参数不正确请使用token,uid 逗号分割")
				}
				userBind := model.UserBindProto{}
				userBind.Cid = this.Conn.Id
				userBind.Uid = splitted[1]
				err = (&websocket2.WebSocketService{}).BindUser(&userBind)

				break
			case "client_bind_room_test":
				testOpen := config.V().GetBool("test.isOpen")
				if !testOpen {
					return errors.New("配置文件test.isOpen 未开启")
				}
				splitted := strings.Split(value, ",")
				if len(splitted) < 3 {
					return errors.New("参数不正确请使用token,uid,roomNo 逗号分割")
				}
				db, _ := connection.GetXorm()
				type User struct {
					Id       string
					Token    string
					Nickname string
					Avatar   string
				}
				user := &User{Id: splitted[1], Token: splitted[0]}
				has, err := db.Table("fa_admin").Get(user)
				if !has {
					return errors.New("token不存在")
				}
				this.Conn.Nickname = user.Nickname
				this.Conn.Avatar = user.Avatar
				this.Conn.RoomNo = splitted[2]
				userBind := model.UserBindProto{}
				userBind.Cid = this.Conn.Id
				userBind.Uid = user.Id

				err = (&websocket2.WebSocketService{}).BindUser(&userBind)
				if err != nil {
					return err
				}
				roomBind := model.RoomBindModel{}
				roomBind.RoomNo = splitted[2]
				roomBind.Uid = userBind.Uid
				roomBind.Avatar = user.Avatar
				roomBind.Nickname = user.Nickname

				err = (&websocket2.WebSocketService{}).BindRoom(&roomBind)
				break
			}
		}
	}
	return err
}

// websocket 请求分发
func (this *RequestHandler) onRequest() error {
	var err error
	conn := this.Conn
	defer func() {
		if err != nil {
			//清除链接状态 以及全局缓存数据
			(&task.Purification{}).CleanConStateWs(conn.Id)
		}
		//conn.Close()
	}()
	for {
		msgbytes, err := conn.ReadMess()
		if err != nil {
			log.Println(err)
			break
		}
		var dataType ProtoModel.DataTypeProto
		var dataBytes []byte
		msgFmt := config.V().GetString("client.msg-fmt")

		if msgFmt == "proto" {
			buffer1 := bytes.NewBuffer(msgbytes)
			//更新心跳检测时间
			conn.UpdateTime()
			unreadBytes, _ := buffer1.ReadByte()
			dataType = ProtoModel.DataTypeProto(unreadBytes)
			dataBytes = buffer1.Bytes()

		} else {
			buffer1 := bytes.NewBuffer(msgbytes)
			//更新心跳检测时间
			conn.UpdateTime()
			unreadBytes, _ := buffer1.ReadByte()
			dataType = ProtoModel.DataTypeProto(unreadBytes)
			dataBytes = buffer1.Bytes()
		}
		//每次接受消息 更新活跃时间
		conn.UpdateTime()
		err = this.onRequestDispatch(dataBytes, dataType, msgFmt)
		//if err != nil {
		//	return err
		//}
	}
	return nil
}

func (this *RequestHandler) onMessage(body []byte, msgFmt string) error {
	return nil
}
func (this *RequestHandler) onPing(body []byte, msgFmt string) error {
	conn := this.Conn
	(&service.Service{}).SendPong(conn.Id, msgFmt)
	return nil
}
func (this *RequestHandler) onPong(body []byte, msgFmt string) error {
	//conn := this.Conn
	//println(conn.Times)
	return nil
}
func (this *RequestHandler) onReply(body []byte, msgFmt string) error {
	return nil
}
func (this *RequestHandler) onSent(body []byte, msgFmt string) error {
	var err error
	sendBody := &ProtoModel.SendBodyProto{}
	if msgFmt == "proto" {
		err = proto.Unmarshal(body, sendBody)
	} else {
		err = json.Unmarshal(body, sendBody)
	}
	if err != nil {
		return err
	}
	err = this.onSentDispatch(sendBody)
	if err != nil {
		return err
	}
	return nil
}

// 链接的命令请求监听
func (this *RequestHandler) onSentDispatch(sendBody *ProtoModel.SendBodyProto) error {
	var err error
	conn := this.Conn
	switch sendBody.GetKey() {
	//登录鉴权
	case "client_bind":
		//ClientBindProto
		clientBind := model.ClientBindProto{}
		err = mapstructure.Decode(sendBody.Data, &clientBind)
		if err != nil {
			return err
		}
		err = (&websocket2.WebSocketService{}).ClientBind(conn, clientBind)
		if err != nil {
			return err
		}
		//房间绑定
	case "room_bind":

		//ClientBindProto
		roomBind := model.RoomBindModel{}
		err = mapstructure.Decode(sendBody.Data, &roomBind)
		if err != nil {
			return err
		}
		err = (&websocket2.WebSocketService{}).RoomBind(conn, roomBind)

	case "room_out":
		err = (&websocket2.WebSocketService{}).RoomOut(conn)

	default:

	}
	return err
}
